#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <strings.h>
#include <signal.h>
#include <netdb.h>
#include <string>
#include <pthread.h>
#include <math.h>

#include "definitions.hpp"

using namespace std;

void* udp_thread_client_func(void* d2){
    
	int sockfd, portno, n;
    struct sockaddr_in serv_addr;
    struct hostent *server;    
    
	uint64_t fsize = d1->getfileSize();
	//cout<<"filesize "<<fsize<<endl;	
    
	portno = d1->getPortNum();
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) 
        error("ERROR opening socket");
    server = gethostbyname((char *)((d1->getHostname()).c_str())); 
    if (server == NULL) {
        fprintf(stderr,"ERROR, no such host\n");
        exit(0);
    }
    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    bcopy((char *)server->h_addr, 
          (char *)&serv_addr.sin_addr.s_addr,
          server->h_length);
    serv_addr.sin_port = htons(portno+1);
    
    unsigned char packet_buf[max_pkt_size];	
    uint64_t f_remain;
    f_remain = 0;
    uint32_t Seq_Number = 1;
    // usleep(250*1000);
    double beginTime = 0;
    beginTime = mytime();
    printf("Begin time is %12.3f\n",beginTime);
    cout<<"Begin Time: "<<beginTime<<endl;
    while (f_remain < fsize) {
        ////cout<<"IUnside while seq number is :"<<Seq_Number<<endl;
        bzero(packet_buf,max_pkt_size);
        pthread_mutex_lock(&chunk_lock);
        fseek(fp,(Seq_Number-1)*1450,SEEK_SET);
        fread(&packet_buf[0],1,max_load_size,fp);
        pthread_mutex_unlock(&chunk_lock);
        
        memcpy(&packet_buf[max_load_size],&Seq_Number,max_hdr_size);
        
        n = sendto(sockfd,packet_buf,max_pkt_size,0,(struct sockaddr *) &serv_addr,sizeof(serv_addr));
        if (n != max_pkt_size)
            error("sendto");   
        /* usleep(100);
         if ((Seq_Number%50000)==0) {
         usleep(2000);
         }*/
        f_remain+=max_load_size;
        Seq_Number = Seq_Number +1;
    }
    
	
    close(sockfd);
    
	while(complete_flag != 1){
        
        continue;
	}
    
    pthread_exit(NULL);
    
}


void* udp_thread_duplicate_client_func(void* d2){
    
	int sockfd, portno, n;
    struct sockaddr_in serv_addr;
    struct hostent *server;    
    
	portno = d1->getPortNum();
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) 
        error("ERROR opening socket");
    server = gethostbyname((char *)((d1->getHostname()).c_str())); 
    if (server == NULL) {
        fprintf(stderr,"ERROR, no such host\n");
        exit(0);
    }
    
    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    bcopy((char *)server->h_addr, 
          (char *)&serv_addr.sin_addr.s_addr,
          server->h_length);
    serv_addr.sin_port = htons(portno+2);
    
    unsigned char packet_buf[max_pkt_size];
    pthread_t udp_nack_thread;
    pthread_attr_t attr_udp_nack;
    int thrd_udp_nack=0;
    // void* status_udp_dup;
    
    pthread_attr_init(&attr_udp_nack);
    pthread_attr_setdetachstate(&attr_udp_nack, PTHREAD_CREATE_JOINABLE);
    //cout<<"Before pthread create udp nack "<<endl;
    thrd_udp_nack=pthread_create(&udp_nack_thread,&attr_udp_nack,udp_nack_client,(void *)sockfd);
    if(thrd_udp_nack){
        cerr<<"Error; return code from thread tcp create call is"<<thrd_udp_nack<<endl;
        exit(-1);
    } 
    
    pthread_attr_destroy(&attr_udp_nack);
    //Wait on signal here from the TCP thread
    while (complete_flag!=1) {
        
        //cout<<"waiting on a signnal ... :P"<<endl;            
        pthread_mutex_lock(&dup_lock);
        pthread_cond_wait(&dup_cond,&dup_lock);
        pthread_mutex_unlock(&dup_lock);
        //cout<<"got a signal ...... "<<endl;
        if (complete_flag==1) {
            break;
        }
        if (dupBuffer1==NULL) {
            continue;
        }
        int k =0;
        while(1) {
            uint32_t Dup_Number;		               
            memcpy(&Dup_Number,&dupBuffer1[k],4);
            //cout<<"DUp_Number:"<<Dup_Number<<endl;
            if(Dup_Number == 0){
                break;
            }
            bzero(packet_buf,max_pkt_size);
            pthread_mutex_lock(&chunk_lock);
            fseek(fp,(Dup_Number-1)*1450,SEEK_SET);
            fread(&packet_buf[0],1,max_load_size,fp);
            pthread_mutex_unlock(&chunk_lock);
            memcpy(&packet_buf[max_load_size],&Dup_Number,4);
            //               
            //cout<<"Writing into duplicate socket .. sedning seq number "<<Dup_Number<<endl;
            n = sendto(sockfd,packet_buf,max_pkt_size,0,(struct sockaddr *) &serv_addr,sizeof(serv_addr));
            if (n != max_pkt_size)
                error("sendto"); 
            k = k+4;
        }
        
        //free(ptr2);
        
    }
    //cout<<"udp_duplicate_client exiting"<<endl;
    
    pthread_exit(NULL);
}


void *udp_nack_client(void* d2){
    
    int portno;
    socklen_t clilen;
    struct sockaddr_in serv_addr, cli_addr;
    int yes=1;
    //cout<<"UDP server thread"<<endl;
    
    int sockfd;
    
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);  
    if(sockfd < 0){
        error("ERROR opening socket");
    }
    
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
                   sizeof(int)) == -1) {
        perror("setsockopt");
        exit(1);
    }
    
    bzero((char *) &serv_addr, sizeof(serv_addr));
    portno = d1->getPortNum();
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(portno+2);
    
    //Binding Socket 
    if (bind(sockfd, (struct sockaddr *) &serv_addr,
             sizeof(serv_addr)) < 0) 
        error("ERROR on binding");
    
    //cout<<"udp_thread_server"<<endl;
    
    int n=0;
    clilen = sizeof(cli_addr);
    
    while(complete_flag!=1){
        
        if(sock_ready_client(sockfd)){
            
            if (complete_flag==1) {
                break;
            }
			n = recvfrom(sockfd,dupBuffer1,2500,0,(struct sockaddr *) &cli_addr,&clilen);
			if(n<=0){
                continue;
			}
            //cout<<"got a packet ...... "<<endl;
            //cout<<"signalling ...... "<<endl;
	        //signal udp client duplicate thread
            pthread_mutex_lock(&dup_lock);
            pthread_cond_signal(&dup_cond);
            pthread_mutex_unlock(&dup_lock);
		}
		else{
			continue;
            
		}
    }
	pthread_mutex_lock(&dup_lock);
    pthread_cond_signal(&dup_cond);
    pthread_mutex_unlock(&dup_lock);
    close(sockfd);  
    //pthread_kill();
    //cout<<"tcp_thread_client exiting"<<endl;
    pthread_exit(NULL);
    
}



